﻿using System;
using System.Collections.Generic;

namespace SharpDepend.Classes.Threading
{
    public class Dispatcher
    {
        private IGuiTimer mTimer;
        private Queue<Action> mQueueGuiDelegates;

        public int ContextThreadId { get; private set; }

        public int CurrentThreadId { get { return System.Threading.Thread.CurrentThread.ManagedThreadId; } }

        public bool IsContextThreadNow { get { return ContextThreadId == CurrentThreadId; } }

        /// <summary>
        /// Create an instance of Discpatcher.
        /// <para>Must be called from main thread.</para>
        /// </summary>
        public Dispatcher()
            : this(100)
        {
        }

        /// <summary>
        /// Create an instance of Discpatcher.
        /// <para>Must be called from main thread.</para>
        /// </summary>
        public Dispatcher(int msInterval)
        {
            mQueueGuiDelegates = new Queue<Action>();

            // Get context thread id.
            ContextThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId;

            mTimer = SharpDepend.Manager.GUITimerManager.CreateTimer();
            mTimer.Interval = new TimeSpan(0, 0, 0, 0, msInterval);
            mTimer.Tick += Timer_Tick;
        }

        /// <summary>
        /// Calls from main thread for a specified time intervall.
        /// </summary>
        private void Timer_Tick()
        {
            if (!IsContextThreadNow)
            {
                throw new InvalidOperationException("Dispatcher instace must be created from main thread.");
            }

            lock (this)
            {
                while (mQueueGuiDelegates.Count > 0)
                {
                    mQueueGuiDelegates.Dequeue()();
                }
            }
        }

        /// <summary>
        /// Start dispatcher.
        /// </summary>
        public void Start()
        {
            mTimer.Start();
        }

        /// <summary>
        /// Stop dispatcher.
        /// </summary>
        public void Stop()
        {
            mTimer.Stop();
        }

        /// <summary>
        /// This will call the Action delegate directly if this is called from main thread.
        /// <para>Otherwise it will be called on a GUI looped timer shortly.</para>
        /// </summary>
        /// <param name="method">Method to call in main thread.</param>
        public void InvokeOnMainThread(Action method)
        {
            if (IsContextThreadNow)
            {
                method();
            }
            else
            {
                // Queue delegate ready to run on GUI timer loop.
                lock (this)
                {
                    mQueueGuiDelegates.Enqueue(method);
                }
            }
        }
    }
}
